//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.richard.library.port.connect;

import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.richard.library.port.connect.enumerate.ErrorCode;
import com.richard.library.port.connect.enumerate.PortType;
import com.richard.library.port.connect.exception.PortException;
import com.richard.library.port.connect.model.PortInfo;
import com.richard.library.port.connect.model.ReturnMessage;
import com.richard.library.port.connect.port.AbsPort;
import com.richard.library.port.connect.port.BluetoothPort;
import com.richard.library.port.connect.port.EthernetPort;
import com.richard.library.port.connect.port.SerialPort;
import com.richard.library.port.connect.port.USBPort;

import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.List;

/**
 * 端口控制
 */
public class PortControl {

    private PortInfo mPortInfo = new PortInfo();
    private AbsPort mPort = null;

    /**
     * 构造
     *
     * @param portType  端口类型
     * @param context   context
     * @param productId usb 产品id
     * @param vendorId  usb 供应商id
     */
    public PortControl(PortType portType, Context context, String productId, String vendorId) {
        this.mPortInfo.setPortType(portType);
        this.mPortInfo.setContext(context);

        if (!TextUtils.isEmpty(productId)) {
            this.mPortInfo.setUsbProductId(Integer.parseInt(productId));
        }

        if (!TextUtils.isEmpty(vendorId)) {
            this.mPortInfo.setUsbVendorId(Integer.parseInt(vendorId));
        }
    }

    /**
     * 构造
     *
     * @param portType    端口类型
     * @param bluetoothID 蓝牙ID
     */
    public PortControl(PortType portType, String bluetoothID) {
        this.mPortInfo.setPortType(portType);
        this.mPortInfo.setBluetoothId(bluetoothID);
    }

    /**
     * 构造
     *
     * @param portType     端口类型
     * @param ethernetIP   网络IP地址
     * @param ethernetPort 网口端口号
     */
    public PortControl(PortType portType, String ethernetIP, int ethernetPort) {
        this.mPortInfo.setPortType(portType);
        this.mPortInfo.setEthernetIP(ethernetIP);
        this.mPortInfo.setEthernetPort(ethernetPort);
    }

    /**
     * 构造
     *
     * @param portType   端口类型
     * @param baudRate   波特率
     * @param serialPath 串口地址
     */
    public PortControl(PortType portType, int baudRate, String serialPath) {
        this.mPortInfo.setPortType(portType);
        this.mPortInfo.setSerialPath(serialPath);
        this.mPortInfo.setBaudRate(baudRate);
    }

    /**
     * 连接并打开端口
     */
    public void connect() throws PortException {
        try {
            this.startConnect(false);
        } catch (Throwable e) {
            Log.e("PortControl", String.format("error:(connect)%s-即将重试连接", e.getMessage()));
            try {
                Thread.sleep(300);
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
            this.startConnect(true);
        }
    }

    /**
     * 开始连接并打开端口
     */
    private void startConnect(boolean isReConnect) throws PortException {
        this.mPortInfo.setReConnect(isReConnect);
        switch (this.mPortInfo.getPortType()) {
            case USB:
                this.connectUSB(this.mPortInfo.getContext(), this.mPortInfo.getUsbProductId(), this.mPortInfo.getUsbVendorId());
                break;
            case Bluetooth:
                this.connectBluetooth(this.mPortInfo.getBluetoothId());
                break;
            case Ethernet:
                this.connectNet(this.mPortInfo.getEthernetIP(), this.mPortInfo.getEthernetPort());
                break;
            case Serial:
                this.connectSerial(mPortInfo.getSerialPath(), mPortInfo.getBaudRate());
                break;
            default:
                throw new PortException("未找到相应的设备连接设备");
        }
    }

    /**
     * 设置USB连接时是否自动请求权限
     */
    void setAutoRequestPermission(boolean isAutoRequestPermission){
        this.mPortInfo.setAutoRequestPermission(isAutoRequestPermission);
    }

    /**
     * 重置端口连接
     */
    private void resetPort() {
        if (this.mPortInfo != null) {
            this.mPortInfo = null;
        }

        this.mPortInfo = new PortInfo();
        if (this.mPort != null) {
            this.mPort.closePort();
            this.mPort = null;
        }
    }

    /**
     * 连接USB端口设备
     *
     * @param context      context
     * @param usbProductId usb产品id
     * @param usbVendorId  usb供应商id
     */
    private void connectUSB(Context context, int usbProductId, int usbVendorId) throws PortException {
//        this.resetPort();

        if (context == null) {
            throw new PortException("Context不能为空");
        }

        if (usbProductId == 0) {
            throw new PortException("usbProductId 不能为空");
        }

        this.mPortInfo.setContext(context);
        this.mPortInfo.setPortType(PortType.USB);
        this.mPortInfo.setUsbProductId(usbProductId);
        this.mPortInfo.setUsbVendorId(usbVendorId);
        this.mPort = new USBPort(this.mPortInfo);

        ReturnMessage result = this.mPort.openPort();
        if (result == null) {
            throw new PortException("未知设备异常");
        }

        if (!ErrorCode.OpenPortSuccess.equals(result.getErrorCode())) {
            throw new PortException("连接设备失败，请检查设备配置是否正确");
        }
    }

    /**
     * 连接蓝牙端口设备
     *
     * @param bluetoothID 蓝牙id
     */
    private void connectBluetooth(String bluetoothID) throws PortException {
//        this.resetPort();

        if (!BluetoothAdapter.checkBluetoothAddress(bluetoothID)) {
            throw new PortException("蓝牙ID错误");
        }

        this.mPortInfo.setBluetoothId(bluetoothID);
        this.mPortInfo.setPortType(PortType.Bluetooth);
        this.mPort = new BluetoothPort(this.mPortInfo);

        ReturnMessage result = this.mPort.openPort();
        if (result == null) {
            throw new PortException("未知设备异常");
        }

        if (!ErrorCode.OpenPortSuccess.equals(result.getErrorCode())) {
            throw new PortException(String.format("连接蓝牙端口失败，请检查[%s]设备配置是否正确", bluetoothID));
        }
    }

    /**
     * 连接网络端口设备
     *
     * @param ip   IP地址
     * @param port 网络端口
     */
    private void connectNet(String ip, int port) throws PortException {
//        this.resetPort();

        if (port <= 0) {
            throw new PortException("网络设备端口号错误");
        }

        try {
            Inet4Address.getByName(ip);
        } catch (UnknownHostException e) {
            throw new PortException(String.format("找不到%s:%s的地址", ip, port));
        }

        this.mPortInfo.setEthernetPort(port);
        this.mPortInfo.setEthernetIP(ip);
        this.mPortInfo.setPortType(PortType.Ethernet);
        this.mPort = new EthernetPort(this.mPortInfo);

        ReturnMessage result = this.mPort.openPort();
        if (result == null) {
            throw new PortException("未知设备异常");
        }

        if (!ErrorCode.OpenPortSuccess.equals(result.getErrorCode())) {
            throw new PortException(String.format("连接IP地址为%s:%s的设备失败，请检查网络或IP配置是否正确", ip, port));
        }
    }

    /**
     * 连接串口端口设备
     *
     * @param serialPath 串口地址
     * @param baudRate   波特率
     */
    private void connectSerial(String serialPath, int baudRate) throws PortException {
//        this.resetPort();

        if (TextUtils.isEmpty(serialPath)) {
            throw new PortException("serialPath 不能为空");
        }

        this.mPortInfo.setPortType(PortType.Serial);
        this.mPortInfo.setSerialPath(serialPath);
        this.mPortInfo.setBaudRate(baudRate);

        this.mPort = new SerialPort(this.mPortInfo);

        ReturnMessage result = this.mPort.openPort();
        if (result == null) {
            throw new PortException("未知设备异常");
        }

        if (!ErrorCode.OpenPortSuccess.equals(result.getErrorCode())) {
            throw new PortException("连接设备失败，请检查设备配置是否正确");
        }
    }

    /**
     * 获取设备连接名称
     */
    public String getDeviceLinkName() {
        if (this.mPortInfo == null) {
            return "";
        }

        switch (this.mPortInfo.getPortType()) {
            case USB:
                return mPortInfo.getUsbPathName();
            case Ethernet:
                return String.format("%s:%s", mPortInfo.getEthernetIP(), mPortInfo.getEthernetPort());
            case Bluetooth:
                return mPortInfo.getBluetoothId();
            case Serial:
                return mPortInfo.getSerialPath();
            case Unknown:
            default:
                return "";
        }
    }


    /**
     * 获取当前连接端口类型
     */
    public PortType getPortType() {
        return mPortInfo.getPortType();
    }

    /**
     * 获取连接端口信息(耗时方法)
     */
    public PortInfo getPortInfo() {
        this.mPortInfo.setOpened(this.mPort.portIsOpen());
        return this.mPortInfo;
    }

    /**
     * 检查设备连接状态
     *
     * @return 是否已连接(耗时方法)
     */
    public boolean checkLinkedState() {
        if (this.mPortInfo == null || this.mPort == null) {
            return false;
        }
        return this.getPortInfo().isOpened();
    }

    /**
     * 是否已连接（非耗时）
     */
    public boolean isLinked() {
        if (this.mPortInfo == null || this.mPort == null) {
            return false;
        }
        return mPort.isOpen();
    }

    /**
     * 断开连接
     */
    public void disconnect() throws PortException {
        if (this.mPort == null) {
            return;
        }

        ReturnMessage result = this.mPort.closePort();
        if (result == null) {
            throw new PortException(String.format("设备为[%s]出现未知设备异常", this.getDeviceLinkName()));
        }

        if (!ErrorCode.ClosePortSuccess.equals(result.getErrorCode())) {
            throw new PortException(String.format("关闭[%s]设备端口发生异常", this.getDeviceLinkName()));
        }

        this.mPort = null;
    }

    /**
     * 处理设备写入数据结果
     */
    private void handleWriteResult(ReturnMessage result) throws PortException {
        if (result == null) {
            throw new PortException(String.format("设备为[%s]出现未知异常", this.getDeviceLinkName()));
        }

        String message = null;
        switch (result.getErrorCode()) {
            case OpenPortFailed:
                message = String.format("打开[%s]设备端口失败", this.getDeviceLinkName());
                break;
            case ClosePortFailed:
                message = String.format("关闭[%s]设备端口失败", this.getDeviceLinkName());
                break;
            case WriteDataFailed:
                message = String.format("向[%s]设备写入数据失败", this.getDeviceLinkName());
                break;
            case ReadDataFailed:
                message = String.format("读取[%s]设备数据失败", this.getDeviceLinkName());
                break;
            case UnknownError:
                message = String.format("[%s]未知错误", this.getDeviceLinkName());
                break;
            default:
                return;
        }

        if (!TextUtils.isEmpty(message)) {
            throw new PortException(message);
        }
    }

    /**
     * 写入数据到当前已连接设备
     *
     * @param data 写入数据
     */
    public int write(List<byte[]> data) throws PortException {
        if (data == null || data.size() <= 0) {
            return -1;
        }

        int writeCount = -1;
        for (byte[] item : data) {
            if (writeCount < 0) {
                writeCount = this.write(item);
            } else {
                writeCount += this.write(item);
            }
        }

        return writeCount;
    }

    /**
     * 写入数据
     *
     * @param data 数据
     */
    public int write(byte[] data) throws PortException {
        if (data == null || data.length <= 0) {
            return -1;
        }

        ReturnMessage message = this.mPort.write(data);
        if (message.getErrorCode() != ErrorCode.WriteDataSuccess) {
            Log.e("PortControl", String.format("error:(write)%s-即将重试连接并写入", message.getErrorStrings()));
            this.connect();
            message = this.mPort.write(data);
        }
        this.handleWriteResult(message);

        return message.getWriteByteCount();
    }

    /**
     * 向端口写入数据
     */
    public int write(int data) throws PortException {
        data &= 255;

        ReturnMessage message = this.mPort.write(data);
        if (message.getErrorCode() != ErrorCode.WriteDataSuccess) {
            Log.e("PortControl", String.format("error:(write)%s-即将重试连接并写入", message.getErrorStrings()));
            this.connect();
            message = this.mPort.write(data);
        }
        this.handleWriteResult(message);

        return message.getWriteByteCount();
    }

    /**
     * 向端口写入数据
     */
    public int write(byte[] data, int offset, int count) throws PortException {
        if (data == null || data.length == 0) {
            return -1;
        }

        ReturnMessage message = this.mPort.write(data, offset, count);
        if (message.getErrorCode() != ErrorCode.WriteDataSuccess) {
            Log.e("PortControl", String.format("error:(write)%s-即将重试连接并写入", message.getErrorStrings()));
            this.connect();
            message = this.mPort.write(data, offset, count);
        }
        this.handleWriteResult(message);

        return message.getWriteByteCount();
    }

    /**
     * 读取端口数据
     */
    public int read(byte[] buffer, int offset, int count) throws PortException {
        ReturnMessage result = this.mPort.read(buffer, offset, count);
        if (result == null) {
            throw new PortException(String.format("设备为[%s]出现未知设备异常", this.getDeviceLinkName()));
        }

        if (!ErrorCode.ReadDataSuccess.equals(result.getErrorCode())) {
            throw new PortException(String.format("读取[%s]设备端口数据失败", this.getDeviceLinkName()));
        }
        return result.getReadByteCount();
    }

    /**
     * 读取端口数据
     */
    public int read(byte[] buffer) throws PortException {
        return this.read(buffer, 0, buffer.length);
    }

    /**
     * 读取端口数据
     */
    public int read() throws PortException {
        byte[] tem = new byte[1];
        this.read(tem, 0, 1);
        return tem[0];
    }

    /**
     * 读取全部可用流数据
     */
    public byte[] readAvailable() throws PortException {
        int size = getReadAvailable();
        if(size == 0){
            return new byte[]{};
        }

        byte[] tem = new byte[size];
        this.read(tem, 0, size);
        return tem;
    }

    /**
     * 获取读取流的可用大小
     */
    public int getReadAvailable() {
        return this.mPort.getReadAvailable();
    }

}
